package com.hjy.backend.dataManager.dataItem;

import com.google.common.primitives.Bytes;
import com.hjy.backend.commons.SubArray;
import com.hjy.backend.dataManager.DataManagerImpl;
import com.hjy.backend.dataManager.page.Page;
import com.hjy.backend.utils.Parser;
import com.hjy.backend.utils.Types;

import java.util.Arrays;

/**
 * DataItem 是 DM 层向上层提供的数据抽象。上层模块通过地址，向 DM 请求到对应的 DataItem，再获取到其中的数据。
 * <p>
 * DataItem 中保存的数据，结构如下：
 * [ValidFlag] [DataSize] [Data]
 * <p>
 * 其中 ValidFlag 占用 1 字节，标识了该 DataItem 是否有效。
 * 删除一个 DataItem，只需要简单地将其有效位设置为 0。
 * DataSize 占用 2 字节，标识了后面 Data 的长度。
 * <p>
 * 在上层模块试图对 DataItem 进行修改时，需要遵循一定的流程：
 * 在修改之前需要调用 before() 方法，
 * 想要撤销修改时，调用 unBefore() 方法，
 * 在修改完成后，调用 after() 方法。
 * 整个流程，主要是为了保存前相数据，并及时落日志。DM 会保证对 DataItem 的修改是原子性的。
 */
public interface DataItem {

    /**
     * 获取 DataItem 的数据
     *
     * @return
     */
    SubArray data();

    /**
     * 修改前调用
     */
    void before();

    /**
     * 撤销修改调用
     */
    void unBefore();

    /**
     * 在修改完成后调用
     *
     * @param xid
     */
    void after(long xid);

    void release();

    void lock();

    void unlock();

    void rLock();

    void rUnLock();

    Page page();

    long getUid();

    byte[] getOldRaw();

    SubArray getRaw();

    public static byte[] wrapDataItemRaw(byte[] raw) {
        byte[] valid = new byte[1];
        byte[] size = Parser.short2Byte((short)raw.length);
        return Bytes.concat(valid, size, raw);
    }

    /**
     * 从页面的offset处解析出 dataItem
     */
    public static DataItem parseDataItem(Page pg, short offset, DataManagerImpl dm) {
        byte[] raw = pg.getData();
        short size = Parser.parseShort(Arrays.copyOfRange(raw, offset+DataItemImpl.OF_SIZE, offset+DataItemImpl.OF_DATA));
        short length = (short)(size + DataItemImpl.OF_DATA);
        long uid = Types.addressToUid(pg.getPageNumber(), offset);
        return new DataItemImpl(new SubArray(raw, offset, offset+length), new byte[length], pg, uid, dm);
    }

    /**
     * 设置该dataItem为不可用
     * @param raw
     */
    public static void setDataItemRawInvalid(byte[] raw) {
        raw[DataItemImpl.OF_VALID] = (byte)1;
    }
}
